package dat215projekt.data;

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.util.Collections;
import java.util.Comparator;
import java.util.LinkedList;
import java.util.List;
import java.util.Scanner;
import javax.swing.ImageIcon;
import se.chalmers.cse.dat215.project.CreditCard;
import se.chalmers.cse.dat215.project.Customer;
import se.chalmers.cse.dat215.project.IMatDataHandler;
import se.chalmers.cse.dat215.project.Order;
import se.chalmers.cse.dat215.project.Product;
import se.chalmers.cse.dat215.project.ProductCategory;
import se.chalmers.cse.dat215.project.ShoppingCart;
import se.chalmers.cse.dat215.project.ShoppingCartListener;
import se.chalmers.cse.dat215.project.ShoppingItem;

public class IMatDataHandlerExtended{
    private static IMatDataHandler handler;
    private static IMatDataHandlerExtended instance;

    private static final String FAVORITE_FILE_NAME = "favorites.txt";
    
    private List<Product> favorites = new LinkedList<Product>();
    private List<FavoriteListener> favoriteListeners = new LinkedList<FavoriteListener>();

    protected IMatDataHandlerExtended(){}

    /**
     * @param p The product to add as favorite
     */
    public void addFavorite(Product p) {
        if (!favorites.contains(p)){
            favorites.add(p);


            for(FavoriteListener l : favoriteListeners)
                l.favoritesChanged();

            // Resort list
            Collections.sort(favorites,favoriteSorter);

        }
    }

    private static final Comparator<Product> favoriteSorter = new Comparator<Product>() {
        public int compare(Product a, Product b) {
            return a.getName().compareTo(b.getName());
        }
    };

    /**
     * @param p The product to remove from favorites
     */
    public void removeFavorite(Product p){
        if(favorites.contains(p)){
            favorites.remove(p);
            
            for(FavoriteListener l : favoriteListeners)
                l.favoritesChanged();

            // Resort list
            Collections.sort(favorites,favoriteSorter);
        }
    }

    public void addFavoriteListener(FavoriteListener listener){
        favoriteListeners.add(listener);
    }
    /*
    public void removeAllFavorites(){
        favorites.clear();
    }*/

    public List<Product> getFavorites(){
        return favorites;
    }

    public boolean isFavorite(Product p) {
        return favorites.contains(p);
    }

    private void loadFavorites(){
        // Load the favorite list
        BufferedReader r = null;
        
        try {
             r = new BufferedReader(new FileReader(handler.imatDirectory()+File.separator+FAVORITE_FILE_NAME));
             String line = null;
             while((line = r.readLine()) != null)
                favorites.add(handler.getProduct(new Scanner(line).nextInt()));
        } catch (IOException e) {
            System.out.println("Was not able to load favorites from file.");
        }
        finally{
            try {
                r.close();
            } catch (Exception e) {}
        }

        // Sort favorites by name
        Collections.sort(favorites,favoriteSorter);
    }

    private void saveFavorites(){
        // Save the favorite list
        
        BufferedWriter w = null;
        try {
             w = new BufferedWriter(new FileWriter(handler.imatDirectory()+File.separator+FAVORITE_FILE_NAME));

             for(Product p : favorites)
                 w.write(p.getProductId()+" "+p.getName()+"\n");
        } catch (IOException e) {
            System.out.println("Was not able to save favorites to file");
        }
        finally{
            try {
                w.close();
            } catch (Exception e) {}
        }
    }

    public Order placeOrder(){
        return handler.placeOrder();
    }

    public Customer getCustomer(){
        return handler.getCustomer();
    }

    public CreditCard getCreditCard(){
        return handler.getCreditCard();
    }

    public boolean isUserComplete(){
        return handler.isUserComplete();
    }

    public synchronized static IMatDataHandlerExtended getInstance() {
        if(instance == null){
            instance = new IMatDataHandlerExtended();
            handler = IMatDataHandler.getInstance();
            instance.loadFavorites();
        }

        return instance;
    }

    public void shutDown(){
        handler.shutDown();
        saveFavorites();
    }

    public Product getProduct(int id){
        return handler.getProduct(id);
    }

    private static final Comparator<Product> productSorter = new Comparator<Product>() {
        public int compare(Product a, Product b) {
            boolean aIsFav = instance.isFavorite(a);
            boolean bIsFav = instance.isFavorite(b);

            // If both or none are favorites, compare name
            if(aIsFav && bIsFav || !aIsFav && !bIsFav)
                return a.getName().compareTo(b.getName());
            else
                if(aIsFav)
                    return -1;
                else
                    return 1;
        }
    };

    /**
     * Returns a sorted product list with favorites first.
     * @param c
     */
    public List<Product> getProducts(ProductCategory c){
        List<Product> products = handler.getProducts(c);
        Collections.sort(products,productSorter);
        return products;
    }

     /**
     * Returns a sorted product list with favorites first.
     * @param c
     */
    public List<Product> getProducts(BaseProductCategory bc){
        // Create list with all products in the basecategory
        List<Product> products = new LinkedList<Product>();

        for(ProductCategory c : BaseProductCategory.getCategories(bc))
            products.addAll(handler.getProducts(c));

        Collections.sort(products,productSorter);
        return products;
    }

    /*
    public ImageIcon getImageIcon(Product p){
        if(handler.hasImage(p))
            return handler.getImageIcon(p);
        else // Question mark if no image exists
            return new ImageIcon("src/dat215projekt/resources/32/question.png");
    }*/
   

    /**
     * Places the a product with the given amount into the shopping cart.<br>
     * If the product already exists in the shopping cart it will be added to the <br>
     * already existing shopping item<p>
     * Only discrete units can be added.....
     * @param p
     * @param amount
     */
    public void addToShoppingCart(Product p, int amount){
        if(amount < 1)
            return;
        
        ShoppingCart cart = handler.getShoppingCart();

        // Check if item already exists
        for(ShoppingItem i : cart.getItems()){
            if(i.getProduct().equals(p)){
                i.setAmount(i.getAmount()+amount);
                
                
                fireShoppingCartChanged();
                return;
            }
        }

        cart.addItem(new ShoppingItem(p,(double)amount));
    }

    public void fireShoppingCartChanged(){
        // Notify listeners
        for(ShoppingCartListener l : cartListeners)
            l.shoppingCartChanged();
    }

    public void removeFromShoppingCart(ShoppingItem s){
        ShoppingCart.getInstance().removeItem(s);
    }

    public void changeShoppingItemAmount(ShoppingItem s, int amount){
        int newAmount = (int)s.getAmount() + amount;

        if(newAmount < 1){
            handler.getShoppingCart().removeItem(s);
            return;
        }

        
        s.setAmount(s.getAmount() + amount);

        fireShoppingCartChanged();
    }


    private List<ShoppingCartListener> cartListeners = new LinkedList<ShoppingCartListener>();
    public void addShoppingCartListener(ShoppingCartListener listener){
        handler.getShoppingCart().addShoppingCartListener(listener);
        cartListeners.add(listener);
    }

    private static Comparator<Order> orderSorter = new Comparator<Order>(){
        public int compare(Order a, Order b) {
            return b.getDate().compareTo(a.getDate());
        }
    };

    public List<Order> getOrders(){
        List<Order> orders = handler.getOrders();
        Collections.sort(orders,orderSorter);
        return orders;
    }

    public ShoppingCart getShoppingCart(){
        return handler.getShoppingCart();
    }

    public List<ShoppingItem> getShoppingCartItems(){
        return handler.getShoppingCart().getItems();
    }

    public double getShoppingCartTotalCost(){
        return handler.getShoppingCart().getTotal();
    }
    /*
    Not needed yet

    public ImageIcon getImageIcon(Product p, Dimension d){
        return handler.getImageIcon(p, d);
    }
    */
    public ImageIcon getImageIcon(Product p, int width, int height){
        return handler.getImageIcon(p, width, height);
    }
}
